/* 算法实现模板 */
import AlgorithmBase from "./AlgorithmBase";
import katex from 'katex';
import Vector, {toVector} from "../../common/Vector";
import { showMathStringInTable } from "../../visual/Table2D";
import { POINT_TYPE } from "../../visual/Canvas2DItem";
import Figure2D from "../../visual/Figure2D";

import DefautValue from "./input/Perceptron.input.json";

class Perceptron extends AlgorithmBase{
  constructor(){
    super('Perceptron');

    // 初始化图
    const figure = new Figure2D(600, 450);
    figure.xMax = 10.0;
    figure.xMin = 0.0;
    figure.yMax = 10.0;
    figure.yMin = 0.0;
    figure.gridX = 12;
    figure.gridY = 12;
    
    this.figure = figure;
    this.trainLog = [];
  }

  init(){
    this.loadModel(DefautValue);

    if(this.domFigure) {
      this.domFigure.innerHTML = '';
      this.domFigure.appendChild(this.figure.domElement);
    }
    this.print();
  }

  // 显示模型
  displayModel(){
    // 渲染到标签
    if(this.domModel){
      let katexString = String.raw`\begin{aligned}`;
      katexString += String.raw`w &= ${this.model.w.toKatexString()}\\`;
      katexString += String.raw`b &= ${this.model.b} \\`;
      katexString += String.raw`\eta &= ${this.model.eta}`
      katexString += String.raw`\end{aligned}`;

      katex.render(katexString, this.domModel, { throwOnError: false });
    }
    // 绘制到图形
    const w = this.model.w;
    const b = this.model.b;
    if(w[1] !== 0 && w[0] !== 0){
      // x1 = 0
      var y1 = -b / w[1];
      // x2 = 0;
      var y2 = -b / w[0];
      if(y1 * y2 <= 0){
        // 计算y2 = 10时
        y2 = (-b-this.figure.yMax*w[1]) / w[0] ;
        this.figure.addLine([0, y1], [y2, this.figure.yMax]);
      }else{
        this.figure.addLine([0, y1], [y2, 0]);
      }
    }
    this.figure.update();
  }
  // 显示训练数据集
  displayTrain(){
    const x = this.trainDS.x;
    const y = this.trainDS.y;

    // 渲染到train div
    if(this.domTrain){
      let katexString = String.raw`T=\{`;
      for(let i = 0, il = x.length; i < il; i++){
        katexString +=`(${x[i].toKatexString()}, ${y[i]})`;
        if(i !== il - 1){
          katexString += ', '
        }
      }
      katexString += String.raw`\}`;

      katex.render(katexString, this.domTrain, { throwOnError: false });

      // 添加点到图
      for(let i = 0, il = x.length; i < il; i++){
        const p = x[i];
        const pt = y[i] === 1?POINT_TYPE.Circle:POINT_TYPE.X_Cross;
        const color = y[i] === 1?'#0000ff':'#ff00ff';
        this.figure.addPoint(p[0], p[1], 5, pt, color);
        // 添加字符串
        this.figure.addString(`(${p[0]},${p[1]})`, p[0], p[1], true, '#990000');
      }
      // 添加Path
      this.figure.addPath([[0.5,0.5], [0.5,10], [10,10], [10, 0.5], [0.5,0.5]]);

      this.figure.update();
    }
  }

  // 从JSON中导入模型
  loadModel(json){
    if(json.TrainDS){
      if(json.TrainDS instanceof Array){
        this.trainDSList = [];
        for(let i = 0, il = json.TrainDS.length; i < il; i++){
          const dat = json.TrainDS[i];
          dat.x = toVector(dat.x);
          this.trainDSList.push(dat);
        }
        this.trainDS = this.trainDSList[0];
      }
      else{
        let trainDS  = json.TrainDS;
        trainDS.x    = toVector(trainDS.x);
        this.trainDS = trainDS;
      }
    }
    
    if(json.Model){
      const model   = json.Model;
      model.w = new Vector(model.w, true);
      this.model = model;
    }
  }

  // 训练
  train(){ 
    const Tx = this.trainDS.x;
    const Ty = this.trainDS.y;

    var w = new Vector([0,0], true);
    var b = 0.0;
    var eta = parseFloat(this.model.eta);

    var count = 0;
    var isDone = false;
    var trainLog = [];    // 训练日志
    trainLog.push([String.raw`\textbf{迭代次数}`, String.raw`\textbf{误分类点}`, 'w', 'b', String.raw`w \cdot x + b`]);
    trainLog.push([`${count}`, '', '0', '0', '0']);

    while(!isDone){
      count++;
      isDone = true;
      let i = 0, il = Tx.length;
      for(; i < il; i++){
        const rst = Ty[i]*(Tx[i].dot(w) + b);
        if(rst <= 0){
          w = w.add(Tx[i].multiply(Ty[i]*eta));
          b = b + Ty[i]*eta;
          isDone = false;

          // 保存到日志
          let tmp = String.raw`${w[0].toFixed(2)}x^{(1)}`;
          for(let xi = 1, xl = w.length; xi < xl; xi++){
            tmp += String.raw`${w[xi]>=0?'+':''}${w[xi].toFixed(2)}x^{(${xi+1})}`;
          }
          tmp += `${b>=0?'+':''}${b.toFixed(2)}`;

          trainLog.push([
            `${count}`, 
            String.raw`x_${i+1}`, 
            w.toKatexString(), 
            `${b}`, 
            tmp,
          ]);
          break;
        }
      }
    }

    let tmp = String.raw`${w[0].toFixed(2)}x^{(1)}`;
    for(let xi = 1, xl = w.length; xi < xl; xi++){
      tmp += String.raw`${w[xi]>=0?'+':''}${w[xi].toFixed(2)}x^{(${xi+1})}`;
    }
    tmp += `${b>=0?'+':''}${b.toFixed(2)}`;

    trainLog.push([
      `${count}`, 
      `0`, 
      w.toKatexString(), 
      `${b}`, 
      tmp,
    ]);

    this.model.w = w;
    this.model.b = b;

    this.print();

    if(this.domTable) this.domTable.innerHTML = '';
    showMathStringInTable(this.domTable, trainLog);
  }

  // 重写print
  print(){
    this.figure.clearAll();
    this.displayModel();
    this.displayTrain();
  }
}


export default Perceptron;